<!-- 该组件 VUE 版本在 3.4 以上可使用-->
<template>
  <Form
    ref="formRef"
    :model="model"
    :label-col="labelCol"
    :wrapper-col='wrapperCol'
    :layout="layout"
    @finish="finish">
    <template v-for="config in newConfigs" :key="config.key">
      <Form.Item v-bind="config.forImteAttrs">
        <component
          :is="config.type"
          v-bind="config.componentAttrs"
          :options="config.componentAttrs.options || options?.[config.key]"
          v-model:[config.model]="model[config.key]"
          v-on='{ change: (e: any) => change(e, config.key) }'>
          {{ config.componentAttrs.buttonText || null }}
        </component>
      </Form.Item>
    </template>
    <slot />
  </Form>
</template>

<script setup lang="ts">
import {
  Form,
  FormInstance,
} from 'ant-design-vue';
import {
  computed, Ref,
  toRefs,
} from 'vue';
import { FormItemConfig } from './ConfigFormType';

const formRef = defineModel<Ref<null | undefined> | FormInstance>('formRef');
const model = defineModel<any>();

interface IConfigFormProps {
  layout?: 'inline' | 'vertical' | 'horizontal';
  FormGrid?: {};
  labelCol?: any;
  wrapperCol?: any;
  configs: FormItemConfig[];
  options?: { [index: string]: any[] }
}
const props = defineProps<IConfigFormProps>();
const {
  configs, layout, FormGrid, labelCol, options, wrapperCol,
} = toRefs(props);

const generateUUID = () => 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
  // eslint-disable-next-line no-bitwise
  const r = Math.random() * 16 | 0;
  // eslint-disable-next-line no-bitwise, no-mixed-operators
  const v = c === 'x' ? r : (r & 0x3) | 0x8;
  return v.toString(16);
});

const newConfigs = computed(() => configs.value.map((config: any) => {
  const {
    grid, key, type, attrs, itemModel, ...rest
  } = config;
  if (key) {
    rest.name = key;
  }
  const forImteAttrs: Partial<any> = rest;
  const componentAttrs = { ...attrs } || {};
  return {
    layout: { ...FormGrid, ...grid },
    key: key || rest.name || generateUUID(),
    type,
    model: itemModel!,
    forImteAttrs,
    componentAttrs,
  };
}));

interface IConfigFormEmits {
  (e: 'finish', value: any): void;
  (e: 'change', value: any): void;
}
const emit = defineEmits<IConfigFormEmits>();
const finish = (value: any) => emit('finish', { value });
const change = (e: any, key: string) => emit('change', { value: e, key });
</script>
